package chap_05_AQS.step_02_StampedLock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * @Description 和 ReentrantReadWriteLock 功能一致的部分
 * @Author shihan
 * @Date 2022/11/19 13:46
 * @Version 1.0
 */
public class StampedLock_demo01 {

    static StampedLock stampedLock = new StampedLock();

    /**
     * 获取写锁：返回戳记（释放锁时需要用），戳记返回 0 表示获取锁失败，其他情况视为获取成功
     * 释放写锁：传入戳记值，并且指明释放的是写锁
     */
    public void write() {
        long stamp = stampedLock.writeLock();
        System.out.println(Thread.currentThread().getName() + "写操作开始...");
        if (0 == stamp) {
            System.out.println(Thread.currentThread().getName() + "写锁获取失败!");
            return;
        }
        System.out.println(Thread.currentThread().getName() + "写操作结束...");
        stampedLock.unlockWrite(stamp);
    }

    /**
     * 获取读锁：返回戳记（释放锁时需要用），戳记返回 0 表示获取锁失败，其他情况视为获取成功
     * 释放读锁：传入戳记值，并且指明释放的是读锁
     */
    public void read() {
        long stamp = stampedLock.readLock();
        System.out.println(Thread.currentThread().getName() + "读操作开始...");
        if (0 == stamp) {
            System.out.println(Thread.currentThread().getName() + "读锁获取失败!");
            return;
        }
        // 阻塞两秒
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "读操作结束...");
        stampedLock.unlockRead(stamp);
    }

    public void tryOptimisticRead() {
        System.out.println(Thread.currentThread().getName() + "-->乐观模式的读锁...");
        // 先返回一个乐观读的戳记，用于后续的 validate，验证期间是否有被修改过
        long stamp = stampedLock.tryOptimisticRead();
        // validate 返回 true 表示期间没有被修改过，false 表示期间已经被修改过
        System.out.println(Thread.currentThread().getName() + "：" + stampedLock.validate(stamp));

        // 阻塞两秒钟，模拟让其他线程进行写操作修改值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // validate 验证发现修改过了，需要自己手动升级成阻塞的读锁处理
        System.out.println(Thread.currentThread().getName() + "：" + stampedLock.validate(stamp));
        if (!stampedLock.validate(stamp)) {
            long newStamp = stampedLock.readLock();
            System.out.println(Thread.currentThread().getName() + "-->乐观模式失败，手动升级为阻塞模式的读锁！");
            // unlock 动作，最好都是放到 try-catch-finally 里面去做
            stampedLock.unlockRead(newStamp);
        }

    }

    public static void main(String[] args) {
        //commonMode();
        StampedLock_demo01 demo01 = new StampedLock_demo01();
        new Thread(() -> {
            demo01.tryOptimisticRead();
        }, "读线程").start();

        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " run ...");
            demo01.write();
        }, "写线程").start();
    }

    /**
     * 和 ReentrantReadWriteLock 一样，读写互斥，只要有读锁存在，写锁就阻塞
     */
    private static void commonMode() {
        StampedLock_demo01 demo01 = new StampedLock_demo01();
        new Thread(() -> {
            demo01.read();
        }, "读线程").start();

        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " run ...");
            demo01.write();
        }, "写线程").start();
    }

}
